package org.fast.develop.logicflow.runtime;

import cn.hutool.core.lang.Assert;
import de.odysseus.el.ExpressionFactoryImpl;
import de.odysseus.el.util.SimpleContext;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.fast.develop.logicflow.core.meta.LogicFlowDefinitionFactory;
import org.fast.develop.logicflow.core.meta.builder.ElementFactory;
import org.fast.develop.logicflow.core.meta.shape.LogicFlowDefinition;
import org.fast.develop.logicflow.runtime.cache.IThreadStore;
import org.fast.develop.logicflow.runtime.context.LogicFlowContext;
import org.fast.develop.logicflow.runtime.context.LogicFlowContextManager;
import org.fast.develop.logicflow.runtime.debug.ExecuteDebugConfig;
import org.fast.develop.logicflow.runtime.executor.InstanceExecutor;
import org.fast.develop.logicflow.runtime.executor.TaskExecutor;
import org.fast.develop.logicflow.runtime.executor.TaskListenerCreator;
import org.fast.develop.logicflow.runtime.expression.DefaultJuelExpressionActuator;
import org.fast.develop.logicflow.runtime.expression.ExpressionExecutor;
import org.fast.develop.logicflow.runtime.expression.ExpressionManager;
import org.fast.develop.logicflow.runtime.instance.LogicFlowInstance;

import javax.el.ELContext;
import javax.el.ExpressionFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author 莫寅
 * @createTime 2022-11-24 11:58
 * @desc：
 */
@Slf4j
public class LogicFlowExecutor {

    private ExpressionExecutor expressionExecutor;

    private LogicFlowInstanceFactory logicFlowInstanceFactory;

    private TaskExecutor taskExecutor;

    private ExpressionFactory expressionFactory = new ExpressionFactoryImpl();

    protected ELContext parsingElContext = createParsingElContext();

    protected ELContext createParsingElContext() {
        return new SimpleContext();
    }

    private TaskListenerCreator taskListenerCreator;

    private DefaultJuelExpressionActuator expressionActuator;

    private InstanceExecutor instanceExecutor;

    private LogicFlowContextManager logicFlowContextManager;

    private IThreadStore threadStore;

    public LogicFlowExecutor(ElementFactory elementFactory, TaskListenerCreator taskListenerCreator, LogicFlowContextManager logicFlowContextManager, IThreadStore threadStore) {
        this.expressionExecutor = new ExpressionExecutor();
        LogicFlowDefinitionFactory logicFlowDefinitionFactory = new LogicFlowDefinitionFactory(elementFactory);
        this.logicFlowInstanceFactory = new LogicFlowInstanceFactory(logicFlowDefinitionFactory);
        this.taskExecutor = new TaskExecutor(taskListenerCreator);
        this.expressionActuator = new DefaultJuelExpressionActuator(new ExpressionManager());
        this.threadStore = threadStore;
        this.instanceExecutor = new InstanceExecutor(expressionActuator, taskExecutor, logicFlowContextManager, threadStore);

        this.logicFlowContextManager = logicFlowContextManager;
    }


    public LogicFlowInstance execute(LogicFlowDefinition logicFlowDefinition) {
        Assert.notNull(logicFlowDefinition, "logicFlowDefinition is not be null");

        LogicFlowInstance logicFlowInstance = logicFlowInstanceFactory.create(logicFlowDefinition);

        LogicFlowContext logicFlowContext = new LogicFlowContext();
        logicFlowContextManager.set(logicFlowContext);

        this.executeByInstance(logicFlowInstance, new HashMap(), ExecuteDebugConfig.OfNull());
        return logicFlowInstance;
    }

    public LogicFlowInstance execute(LogicFlowDefinition logicFlowDefinition, Map variables) {
        return execute(logicFlowDefinition, variables, ExecuteDebugConfig.OfNull());
    }

    public LogicFlowInstance execute(LogicFlowDefinition logicFlowDefinition, Map variables, ExecuteDebugConfig debugConfig) {
        Assert.notNull(logicFlowDefinition, "logicFlowDefinition is not be null");

        LogicFlowInstance logicFlowInstance = logicFlowInstanceFactory.create(logicFlowDefinition);
        this.executeByInstance(logicFlowInstance, variables, debugConfig);
        return logicFlowInstance;
    }

    public LogicFlowInstance execute(String jsonSchema) {
        Assert.notNull(jsonSchema, "Json is not be null");

        return execute(jsonSchema, new HashMap());
    }

    public LogicFlowInstance execute(String jsonSchema, Map variables) {
        Assert.notNull(jsonSchema, "Json is not be null");

        LogicFlowInstance logicFlowInstance = logicFlowInstanceFactory.create(jsonSchema);

        this.executeByInstance(logicFlowInstance, variables, ExecuteDebugConfig.OfNull());
        return logicFlowInstance;
    }

    @SneakyThrows
    private LogicFlowInstance executeByInstance(LogicFlowInstance logicFlowInstance, Map variables, ExecuteDebugConfig executeDebugConfig) {
        LogicFlowContext logicFlowContext = new LogicFlowContext();
        logicFlowContext.setExecuteDebugConfig(executeDebugConfig);
        logicFlowContextManager.set(logicFlowContext);
        Exception exception = null;
        try {
            instanceExecutor.execute(logicFlowInstance, variables, logicFlowContext);
        } catch (Exception e) {
            exception = e;
//            throw e;
        } finally {
            logicFlowContextManager.remove();
            if(Objects.nonNull(exception)){
                throw exception;
            }
            return logicFlowInstance;

        }
    }


}
